using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Asteroids
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        double oldPct = 0;
        #region Constants

        //Menu constants
        public const int BUTTON_WIDTH = 150;
        public const int BUTTON_HEIGHT = 50;
        public const int NUM_BUTTONS = 14;
        public const int SYSTEM_COL = 0; //column for system buttons
        public const int SYSTEM_BUTTONS = 4; //Save, Load, Exit
        public const int UPGRADE_COL = 1;
        public const int UPGRADE_BUTTONS = 4;
        public const int ATTRIB_COL = 2;
        public const int ATTRIB_BUTTONS = 4;

        public const int FULLSCREEN = 0;
        public const int SAVE = 1;
        public const int LOAD = 2;
        public const int EXIT = 3;

        public const int LEFT = 0;
        public const int RIGHT = 1;
        public const int UP = 2;
        public const int DOWN = 3;


        //Upgrade constants
        public const int FIRST_BUYABLE = 4;//All upgrades found at indexes before this are actually attribute upgrades.
        public const int NUM_UPGRADES = 9;

        //Spawn constants
        public const int MAX_ENEMIES = 20;
        public const int MAX_ASTEROIDS = 30;
        public const int SPAWN_ZONE = 1; //this number * size of window is the limit size

        //Other constants?
        public const double ENEMY_FIRE_CHANCE = 0.05; //If an enemy is selected to fire, this gives the chance they will fire.
        public readonly TimeSpan MESSAGE_DURATION = TimeSpan.FromSeconds(4); //How long a message is on screen.
        #endregion 

        #region Templates

        public static ItemModel[] shipModels = new ItemModel[6]; //3 = number of ship types
        public static ItemModel[] asteroidTypes = new ItemModel[2]; //1 = number of asteroid types
        public static ItemModel[] explosionTypes = new ItemModel[1];
        public static ItemModel[] bulletTypes = new ItemModel[1];
        //Can be removed if no other types are made

        public static Upgrade[] upgrades = new Upgrade[NUM_UPGRADES];

        public static SoundEffect[] sounds = new SoundEffect[5];

        //Constant item names
        //Ships
        public const int PLAYERSHIP1 = 0;
        public const int PLAYERSHIPE = 1;
        public const int ENEMYSHIP1 = 2;
        public const int ENEMYSHIP2 = 3;
        public const int ENEMYSHIP3 = 4;
        public const int ENEMYSHIP4 = 5;

        //Attributes
        public const int HEALTH = 0;
        public const int SPEED = 1;
        public const int ACCEL = 2;
        public const int MASS = 3;
        //Upgrades
        public const int STOP_ASTEROIDS = 4;
        public const int ASTEROID_GUN = 5;
        public const int EXPLODE_ASTEROIDS = 6;
        public const int MAGNET_BEAM = 7;

        //Asteroids 
        public const int BASIC = 0;
        public const int ASTEROID_BULLET = 1;

        //Bullets
        public const int BULLET = 0;

        //Sounds
        public const int EXPLOSION = 0;
        public const int COLLISION = 1;
        public const int ENEMYSHOOT = 2;
        public const int BEAMSOUND = 3;
        public const int BULLETHIT = 4;

        //Model stats
        private void initTemplates()
        {
            //SHIPS

            //PLAYERSHIP1
            shipModels[PLAYERSHIP1].accelFactor = 0.2f;
            shipModels[PLAYERSHIP1].health = 100;
            shipModels[PLAYERSHIP1].mass = 1;
            shipModels[PLAYERSHIP1].maxSpeed = 5;
            shipModels[PLAYERSHIP1].radius = 32;
            

            //ENEMYSHIP1
            shipModels[ENEMYSHIP1].accelFactor = 0.2f;
            shipModels[ENEMYSHIP1].health = 4;
            shipModels[ENEMYSHIP1].mass = 4;
            shipModels[ENEMYSHIP1].maxSpeed = 1;
            shipModels[ENEMYSHIP1].radius = 25;
            shipModels[ENEMYSHIP1].gold = 50;
            shipModels[ENEMYSHIP1].exp = 25;
            shipModels[ENEMYSHIP1].moveChance = 1;

            //ENEMYSHIP2
            shipModels[ENEMYSHIP2].accelFactor = 0.2f;
            shipModels[ENEMYSHIP2].health = 10;
            shipModels[ENEMYSHIP2].mass = 4.5f;
            shipModels[ENEMYSHIP2].maxSpeed = 1.5f;
            shipModels[ENEMYSHIP2].radius = 30;
            shipModels[ENEMYSHIP2].gold = 75;
            shipModels[ENEMYSHIP2].exp = 35;
            shipModels[ENEMYSHIP2].moveChance = 1;

            //ENEMYSHIP3
            shipModels[ENEMYSHIP3].accelFactor = 0.2f;
            shipModels[ENEMYSHIP3].health = 5;
            shipModels[ENEMYSHIP3].mass = 1;
            shipModels[ENEMYSHIP3].maxSpeed = 7f;
            shipModels[ENEMYSHIP3].radius = 20;
            shipModels[ENEMYSHIP3].gold = 100;
            shipModels[ENEMYSHIP3].exp = 40;
            shipModels[ENEMYSHIP3].moveChance = 3;

            //ENEMYSHIP4
            shipModels[ENEMYSHIP4].accelFactor = 0.2f;
            shipModels[ENEMYSHIP4].health = 20;
            shipModels[ENEMYSHIP4].mass = 6;
            shipModels[ENEMYSHIP4].maxSpeed = 2f;
            shipModels[ENEMYSHIP4].radius = 32;
            shipModels[ENEMYSHIP4].gold = 125;
            shipModels[ENEMYSHIP4].exp = 50;
            shipModels[ENEMYSHIP4].moveChance = 1;

            //ASTEROIDS

            //Basic 
            asteroidTypes[BASIC].health = 10;
            asteroidTypes[BASIC].mass = 2;
            asteroidTypes[BASIC].maxSpeed = 5;
            asteroidTypes[BASIC].radius = 25;

            //bullet
            asteroidTypes[ASTEROID_BULLET].health = 4;
            asteroidTypes[ASTEROID_BULLET].mass = 0.1f;
            asteroidTypes[ASTEROID_BULLET].maxSpeed = 7;
            asteroidTypes[ASTEROID_BULLET].radius = 10;
            asteroidTypes[ASTEROID_BULLET].isBullet = true;

            //bullet
            bulletTypes[BULLET].maxSpeed = 7;
            bulletTypes[BULLET].radius = 5;
            bulletTypes[BULLET].isBullet = true;

            //UPGRADES
            
            //Attributes
            upgrades[HEALTH].name = "Health";
            upgrades[SPEED].name = "Speed";
            upgrades[ACCEL].name = "Acceleration";
            upgrades[MASS].name = "Mass";

            //Upgrades
            upgrades[STOP_ASTEROIDS].name = "Stop Asteroids";
            upgrades[STOP_ASTEROIDS].shortName = "STP";
            upgrades[STOP_ASTEROIDS].cost = 300;
            upgrades[STOP_ASTEROIDS].maxLevel = 3;
            upgrades[STOP_ASTEROIDS].cooldown = TimeSpan.FromSeconds(10);
            upgrades[STOP_ASTEROIDS].range = 300;
            upgrades[STOP_ASTEROIDS].cooldownDec = TimeSpan.FromSeconds(3);

            upgrades[ASTEROID_GUN].name = "Asteroid Gun";
            upgrades[ASTEROID_GUN].shortName = "GUN";
            upgrades[ASTEROID_GUN].cost = 1500;
            upgrades[ASTEROID_GUN].maxLevel = 3;
            upgrades[ASTEROID_GUN].cooldown = TimeSpan.FromSeconds(1);
            upgrades[ASTEROID_GUN].range = 100;
            upgrades[ASTEROID_GUN].cooldownDec = TimeSpan.FromSeconds(0.3);

            upgrades[EXPLODE_ASTEROIDS].name = "Explode Asteroids";
            upgrades[EXPLODE_ASTEROIDS].shortName = "XPL";
            upgrades[EXPLODE_ASTEROIDS].cost = 1000;
            upgrades[EXPLODE_ASTEROIDS].maxLevel = 3;
            upgrades[EXPLODE_ASTEROIDS].cooldown = TimeSpan.FromSeconds(10);
            upgrades[EXPLODE_ASTEROIDS].range = 100;
            upgrades[EXPLODE_ASTEROIDS].cooldownDec = TimeSpan.FromSeconds(3);

            upgrades[MAGNET_BEAM].name = "Magnet Beam";
            upgrades[MAGNET_BEAM].shortName = "MAG";
            upgrades[MAGNET_BEAM].cost = 500;
            upgrades[MAGNET_BEAM].maxLevel = 3;
            upgrades[MAGNET_BEAM].cooldown = TimeSpan.FromSeconds(60);
            upgrades[MAGNET_BEAM].range = 300;
            upgrades[MAGNET_BEAM].cooldownDec = TimeSpan.FromSeconds(19);
            upgrades[MAGNET_BEAM].drawTime = TimeSpan.FromSeconds(.08);
        }
        #endregion

        #region Variables
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        BasicEffect basicEffect;

        Camera camera;
        Background mainBackground;
        Texture2D backgroundColor;
        Random random;
        Player player;
        int selectedUpgrade = ASTEROID_GUN;

        List<SpaceItem> objectList;
        List<SpaceItem> killList = new List<SpaceItem>(); //lists to hold the game's SpaceItems
        List<Explosion> explosionList;
        List<Bullet> bulletList;
        List<Notification> notifications = new List<Notification>();
        Texture2D[] upgradeIcons = new Texture2D[4];

        Texture2D titleScreen;
        Texture2D gameOver;
        Texture2D genericTexture; //This can be stretched for form a single color rectangle

        SpriteFont font; //Currently game's only font
        SpriteFont msgFont; //The other only font. This is used to draw error messages to the screen.

        //Counters to know when to spawn new SpaceItems
        int enemyCount = 0;
        int asteroidCount = 0;
        int playerBulletCount = 0;
        int cooldownToDraw = -1;
        
        //Menu Management
        int selectedButton;
        Boolean gameStarted = false; //true if title screen is passed
        Boolean escDown = false; //Needed for pause
        Boolean gamePaused = false;
        
        Boolean uDown = false;
        Boolean dDown = false;
        Boolean rDown = false;
        Boolean lDown = false;
        Boolean entDown = false;
        Boolean spcDown = false;
        Boolean n1Down = false;
        Boolean n2Down = false;
        Boolean n3Down = false;
        Boolean n4Down = false;
        Boolean[] buttonsActive = new Boolean[NUM_BUTTONS];

        Boolean nuxMode = false; //on for easy mode
        String saveLoc = @"save.fas";
        Boolean explOut = false;

        public static int playerLevelUps;
        TimeSpan gameEndedAt;
        Boolean gameEnded;


        #endregion

        #region Initiate

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferWidth = 1280;
            graphics.PreferredBackBufferHeight = 720;
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            objectList = new List<SpaceItem>();
            explosionList = new List<Explosion>();
            bulletList = new List<Bullet>();
            mainBackground = new Background();
            //explosions = new Texture2D[1];
            initTemplates();

            //System buttons
            for (int i = 0; i < 4; i++)
            {
                buttonsActive[i] = true;
            }

            //Upgrade buttons
            for (int i = 4; i < NUM_BUTTONS; i++)
            {
                buttonsActive[i] = false;
            }

            // Initialize our random number generator
            random = new Random();

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            camera = new Camera(graphics.GraphicsDevice.Viewport);

            basicEffect = new BasicEffect(spriteBatch.GraphicsDevice);
            basicEffect.VertexColorEnabled = true;
            basicEffect.Projection = Matrix.CreateOrthographicOffCenter
                (0, spriteBatch.GraphicsDevice.Viewport.Width,
                spriteBatch.GraphicsDevice.Viewport.Height, 0,
                0, 1);

            backgroundColor = Content.Load<Texture2D>(@"Textures\SpaceColor");
            mainBackground.Initialize(Content, @"Textures\PrimaryBackground", GraphicsDevice.Viewport);
            
            //load font 
            font = Content.Load<SpriteFont>(@"Fonts\Miramonte");
            msgFont = Content.Load<SpriteFont>(@"Fonts\Miramonte");

            //load ship textures
            shipModels[PLAYERSHIP1].texture = Content.Load<Texture2D>(@"Textures\Player");
            shipModels[PLAYERSHIPE].texture = Content.Load<Texture2D>(@"Textures\PlayerRed");
            shipModels[ENEMYSHIP1].texture = Content.Load<Texture2D>(@"Textures\Enemy");
            shipModels[ENEMYSHIP2].texture = Content.Load<Texture2D>(@"Textures\Enemy2");
            shipModels[ENEMYSHIP3].texture = Content.Load<Texture2D>(@"Textures\Enemy3");
            shipModels[ENEMYSHIP4].texture = Content.Load<Texture2D>(@"Textures\Enemy4");

            //load asteroid textures
            asteroidTypes[BASIC].texture = Content.Load<Texture2D>(@"Textures\Asteroid");
            asteroidTypes[ASTEROID_BULLET].texture = Content.Load<Texture2D>(@"Textures\AsteroidGun");
            
            //load explosion textures
            explosionTypes[0].texture = Content.Load<Texture2D>(@"Textures\Explosion");

            //load bullet textures
            bulletTypes[0].texture = Content.Load<Texture2D>(@"Textures\Bullet");

            //load title screen image
            titleScreen = Content.Load<Texture2D>(@"Textures\TitleScreen");
            gameOver = Content.Load<Texture2D>(@"Textures\GameOver");

            //load upgrade button images
            genericTexture = new Texture2D(GraphicsDevice, 1, 1);
            genericTexture.SetData(new Color[] { Color.White });

            //load upgrade icons
            upgradeIcons[STOP_ASTEROIDS - FIRST_BUYABLE] = Content.Load<Texture2D>(@"Textures\iconSA");
            upgradeIcons[ASTEROID_GUN - FIRST_BUYABLE] = Content.Load<Texture2D>(@"Textures\iconAG");
            upgradeIcons[EXPLODE_ASTEROIDS - FIRST_BUYABLE] = Content.Load<Texture2D>(@"Textures\iconEA");
            upgradeIcons[MAGNET_BEAM - FIRST_BUYABLE] = Content.Load<Texture2D>(@"Textures\iconMB");

            //load sounds
            sounds[EXPLOSION] = Content.Load<SoundEffect>(@"Sounds\battle003");
            sounds[COLLISION] = Content.Load<SoundEffect>(@"Sounds\hit");
            sounds[ENEMYSHOOT] = Content.Load <SoundEffect>(@"Sounds\scifi002");
            sounds[BULLETHIT] = Content.Load<SoundEffect>(@"Sounds\bulletHit");
            sounds[BEAMSOUND] = Content.Load<SoundEffect>(@"Sounds\beam");

            upgrades[MAGNET_BEAM].texture = Content.Load<Texture2D>(@"Textures\Beam");

            player = new Player(PLAYERSHIP1,Vector2.Zero,Vector2.Zero);
            objectList.Add(player);
            if (nuxMode)
            {
                player.health = 9001;
                player.gold = 9001;
                shipModels[PLAYERSHIP1].health = 9001;
            }
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }
        #endregion

        #region Update
        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            
            KeyboardState keystate = Keyboard.GetState();

            if (gameStarted)
            {
                
                if (gameEndedAt.TotalSeconds != 0 && gameTime.TotalGameTime - gameEndedAt > TimeSpan.FromSeconds(5))
                {
                    gameStarted = false;
                    gameEnded = true;
                }

                
                //pause/unpause game
                if (keystate.IsKeyDown(Keys.Escape))
                {
                    escDown = true;
                }
                if (keystate.IsKeyUp(Keys.Escape) && escDown)
                {
                    gamePaused = !gamePaused;

                    escDown = false;
                    selectedButton = 0;
                }
                if(gamePaused)
                    updateButtons();

                

                #region if (gamePaused)
                if (gamePaused)
                {
                    if(keystate.IsKeyDown(Keys.Down)){
                        dDown = true;
                    }
                    
                    if(keystate.IsKeyDown(Keys.Up)){
                        uDown = true;
                    }
                   
                    if (keystate.IsKeyDown(Keys.Enter))
                    {
                        entDown = true;
                    }

                    if (keystate.IsKeyDown(Keys.Right))
                    {
                        rDown = true;
                    }

                    if (keystate.IsKeyDown(Keys.Left))
                    {
                        lDown = true;
                    }

                    if (keystate.IsKeyUp(Keys.Down) && dDown)
                    {
                        dDown = false;
                        uDown = false;
                        entDown = false;
                        rDown = false;
                        lDown = false;
                        
                        selectedButton = findNewIndex(selectedButton, DOWN);
                    }
                    
                    if (keystate.IsKeyUp(Keys.Up) && uDown)
                    {
                        dDown = false;
                        uDown = false;
                        entDown = false;
                        rDown = false;
                        lDown = false;

                        selectedButton = findNewIndex(selectedButton, UP);
                    }

                    if (keystate.IsKeyUp(Keys.Right) && rDown)
                    {
                        dDown = false;
                        uDown = false;
                        entDown = false;
                        rDown = false;
                        lDown = false;

                        selectedButton = findNewIndex(selectedButton, RIGHT);
                    }

                    if (keystate.IsKeyUp(Keys.Left) && lDown)
                    {
                        dDown = false;
                        uDown = false;
                        entDown = false;
                        rDown = false;
                        lDown = false;

                        selectedButton = findNewIndex(selectedButton, LEFT);
                    }
                    
                    if (keystate.IsKeyUp(Keys.Enter) && entDown)
                    {
                        dDown = false;
                        uDown = false;
                        entDown = false;
                        rDown = false;
                        lDown = false;
                        
                        if(buttonsActive[selectedButton])
                            buttonAction(selectedButton);
                    }
                #endregion

                }
                else
                {
                    spawn();
                    //Check upgrade usage
                    if (keystate.IsKeyDown(Keys.D1) && !n1Down)
                    {
                        n1Down = true;
                        selectedUpgrade = STOP_ASTEROIDS;
                    }
                    if (keystate.IsKeyDown(Keys.D2) && !n2Down)
                    {
                        n2Down = true;
                        selectedUpgrade = ASTEROID_GUN;
                    }
                    if (keystate.IsKeyDown(Keys.D3) && !n3Down)
                    {
                        n3Down = true;
                        selectedUpgrade = EXPLODE_ASTEROIDS;
                    }
                    if (keystate.IsKeyDown(Keys.D4) && !n4Down)
                    {
                        n4Down = true;
                        selectedUpgrade = MAGNET_BEAM;
                    }

                    if (keystate.IsKeyDown(Keys.Space) && !spcDown)
                    {
                        spcDown = true;
                        playerAction(gameTime);
                    }

                    //Unset buttons if they have been released.
                    if (keystate.IsKeyUp(Keys.D1))
                        n1Down = false;
                    if (keystate.IsKeyUp(Keys.D2))
                        n2Down = false;
                    if (keystate.IsKeyUp(Keys.D3))
                        n3Down = false;
                    if (keystate.IsKeyUp(Keys.D4))
                        n4Down = false;
                    if (keystate.IsKeyUp(Keys.Space))
                        spcDown = false;

                    Vector2 test; //required as parameter, not really used
                    
                    for (int i = 0; i < objectList.Count; i++)
                    {
                        for (int j = i + 1; j < objectList.Count; j++)
                        {
                            if (CollisionChecker.collide(objectList[i], objectList[j], out test))
                            {
                                if (objectList[i].touched)
                                    objectList[j].touched = true;
                                else if (objectList[j].touched)
                                    objectList[i].touched = true;

                                if (objectList[i] is Player && objectList[j] is Asteroid && !((Asteroid)objectList[j]).isBullet && objectList[i].exploder)
                                {
                                    objectList[j].exploder = true;
                                    objectList[i].exploder = false;
                                    explOut = true;
                                }

                                if (objectList[j] is Player && objectList[i] is Asteroid && !((Asteroid)objectList[i]).isBullet && objectList[j].exploder)
                                {
                                    objectList[i].exploder = true;
                                    objectList[j].exploder = false;
                                    explOut = true;
                                }

                                if (objectList[i].health <= 0)
                                {
                                     killList.Add(objectList[i]);
                                    if (objectList[i] is Enemy && (objectList[i].touched || objectList[j].touched))
                                        giveKillReward((Enemy)objectList[i]);
                                    
                                }
                                else if (objectList[j].health <= 0)
                                {
                                    killList.Add(objectList[j]);
                                    if (objectList[j] is Enemy && (objectList[i].touched || objectList[j].touched))
                                        giveKillReward((Enemy)objectList[j]);
                                }
                                else if (onScreen(objectList[i]) || onScreen(objectList[j])){
                                        sounds[COLLISION].Play();
                                }
                                   
                            }
                        }
                    }

                    movePlayer(keystate);

                    foreach (SpaceItem item in objectList)
                    {
                        if (item is Asteroid && ((Asteroid)item).isMagnetized)
                        {

                            float smallDist = -1;
                            float dist;
                            foreach (SpaceItem targ in objectList)
                            {
                                if (targ is Enemy)
                                {
                                    dist = (float)Math.Sqrt((targ.centerPosition.X - item.centerPosition.X) * (targ.centerPosition.X - item.centerPosition.X) + (targ.centerPosition.Y - item.centerPosition.Y) * (targ.centerPosition.Y - item.centerPosition.Y));

                                    if (dist < smallDist || smallDist == -1)
                                    {
                                        smallDist = dist;
                                       ((Asteroid)item).target = (Enemy)targ;
                                    }
                                }
                            }

                            float x = ((Asteroid)item).target.centerPosition.X - item.centerPosition.X;
                            float y = ((Asteroid)item).target.centerPosition.Y - item.centerPosition.Y;

                            item.ratioVelocity = new Vector2(x, y);
                        }
                        item.Update(gameTime);
                    }

                    for (int i = 0; i < explosionList.Count; i++ )
                    {
                        if (explosionList[i].doneAnimating)
                        {
                            explosionList.RemoveAt(i);
                            i--;
                        }
                        else
                        {
                            explosionList[i].Update(gameTime);
                        }
                    }

                    foreach (Bullet b in bulletList)
                    {
                        b.Update(gameTime);
                    }
                    //Choose a random enemy to see if it is onScreen. If so, it may or may not fire at the player.
                    int randEnemyIndex = random.Next(enemyCount);
                    int currentEnemy = 0;
                    for (int i = 0; i < objectList.Count; i++)
                    {
                        if (objectList[i] is Enemy)
                            currentEnemy++;
                        if (currentEnemy == randEnemyIndex + 1)
                        {
                            currentEnemy = i;
                            break;
                        }
                    }
                    //See if enemy will choose to attack.
                    if (objectList[currentEnemy] is Enemy && onScreen(objectList[currentEnemy]) && random.NextDouble() < ENEMY_FIRE_CHANCE)
                    {
                        //And attack.
                        bulletList.Add(((Enemy)objectList[currentEnemy]).attackPlayer(player));
                        sounds[ENEMYSHOOT].Play();
                    }
                    //Finally, do special (sans-physics) collision for bullets and Objects. Remove any bullets that hit objects.
                    for (int i = 0; i < bulletList.Count; i++)
                    {
                        foreach (SpaceItem item in objectList)
                        {
                            if (bulletList[i].collidesWith(item))
                            {
                                item.health -= bulletList[i].damage;
                                bulletList.RemoveAt(i);
                                sounds[BULLETHIT].Play();
                                i--;
                                break;
                            }
                        }
                    }

                    foreach (SpaceItem item in killList)
                    {
                        explosionList.Add(item.Die());
                        
                        if (item is Asteroid)
                            asteroidCount--;
                        else if (item is Enemy)
                            enemyCount--;

                        if(item is Asteroid && item.exploder)
                            explOut = false;

                        if (onScreen(item))
                            sounds[EXPLOSION].Play();
                        if (item is Player)
                            gameEndedAt = gameTime.TotalGameTime;

                        

                        objectList.Remove(item);
                    }
                    killList = new List<SpaceItem>();

                    //Remove notifications until they all fit on screen.
                    int fontHeight = 2 + (int)msgFont.MeasureString("T").Y;
                    while (notifications.Count * fontHeight > (int)(camera.screenBounds.Height / 2.4))
                    {
                        notifications.RemoveAt(0);
                    }
                    //Remove any messages that have expired.
                    while (notifications.Count > 0 && gameTime.TotalGameTime - notifications[0].creationTime > MESSAGE_DURATION)
                    {
                        notifications.RemoveAt(0);
                    }

                    camera.LookAt(player);
                    
                    despawn();
                    mainBackground.Update(camera);
                }
            }
            else if (gameEnded)
            {
                if (gameEndedAt.TotalSeconds != 0 && gameTime.TotalGameTime - gameEndedAt > TimeSpan.FromSeconds(10))
                { 

                    if (nuxMode)
                    {
                        player.health = 9001;
                        player.gold = 9001;
                        shipModels[PLAYERSHIP1].health = 9001;
                    }

                    player.health = shipModels[PLAYERSHIP1].health;
                    player.accelFactor = shipModels[PLAYERSHIP1].accelFactor;
                    player.centerPosition = new Vector2(0, 0);
                    player.level = 0;
                    player.exp = 0;
                    upgrades.CopyTo(player.upgrades, 0);
                    player.updateStats();
                    playerLevelUps = 0;
                    objectList = new List<SpaceItem>();
                    asteroidCount = 0;
                    enemyCount = 0;
                    cooldownToDraw = -1;

                    objectList.Add(player);
                    gameEndedAt = TimeSpan.FromSeconds(0);
                    gameStarted = false;
                    gameEnded = false;                 
                    
                }
                
            }
            else if (keystate.IsKeyDown(Keys.Enter))
            {
                gameStarted = true;
            }

            base.Update(gameTime);
        }

        protected void spawn()
        {
            //find spawning zone
            Rectangle bounds = camera.screenBounds;
            Vector2 position, velocity;
            Random spawnRandom;
            Rectangle spawnArea = new Rectangle(bounds.Left - bounds.Width * SPAWN_ZONE, bounds.Top - bounds.Height * SPAWN_ZONE, 
                bounds.Height * (SPAWN_ZONE + 1) * 2, bounds.Width * (SPAWN_ZONE + 1) * 2);
            int enemyType = ENEMYSHIP1;
            //spawn asteroids
            while (asteroidCount < MAX_ASTEROIDS)
            {
                spawnRandom = new Random();
                do
                {
                    position = new Vector2(random.Next(spawnArea.Left, spawnArea.Right), random.Next(spawnArea.Top, spawnArea.Bottom));
                } while ((bounds.Left < position.X && bounds.Right > position.X) && (bounds.Top < position.Y && bounds.Bottom > position.Y));

                velocity = new Vector2(random.Next(-1, 1), random.Next(-1, 1));
                objectList.Add(new Asteroid(BASIC, position, velocity));
                asteroidCount++;
            }

            while (enemyCount < MAX_ENEMIES)
            {
                spawnRandom = new Random();

                do
                {
                    position = new Vector2(random.Next(spawnArea.Left, spawnArea.Right), random.Next(spawnArea.Top, spawnArea.Bottom));
                } while ((bounds.Left < position.X && bounds.Right > position.X) && (bounds.Top < position.Y && bounds.Bottom > position.Y));

                velocity = new Vector2(random.Next(-1, 1), random.Next(-1, 1));

                if (player.level >= 2)
                {
                    //pick an enemy type to spawn based on player's level
                    if (spawnRandom.Next(10) < player.level)
                        enemyType = ENEMYSHIP2;

                    if (spawnRandom.Next(20) < player.level)
                        enemyType = ENEMYSHIP3;

                    if (spawnRandom.Next(50) < player.level)
                        enemyType = ENEMYSHIP4;
                }


                objectList.Add(new Enemy(enemyType, position, velocity));
                enemyCount++;
            }
        }

        protected void despawn()
        {
            List<SpaceItem> pruneList = new List<SpaceItem>();
            Rectangle bounds = camera.screenBounds;

            foreach (SpaceItem item in objectList)
            {
                if ((item.position.X < bounds.Left - bounds.Width * SPAWN_ZONE) || (item.position.X > bounds.Right + bounds.Width * SPAWN_ZONE) 
                    || (item.position.Y < bounds.Top - bounds.Height * SPAWN_ZONE) || (item.position.Y > bounds.Bottom + bounds.Height * SPAWN_ZONE))
                    pruneList.Add(item);
            }

            foreach (Bullet b in bulletList)
            {
                if ((b.position.X < bounds.Left - bounds.Width * SPAWN_ZONE) || (b.position.X > bounds.Right + bounds.Width * SPAWN_ZONE)
                    || (b.position.Y < bounds.Top - bounds.Height * SPAWN_ZONE) || (b.position.Y > bounds.Bottom + bounds.Height * SPAWN_ZONE))
                    pruneList.Add(b);
            }


            foreach (SpaceItem item in pruneList)
            {
                objectList.Remove(item);

                if (item is Enemy)
                    enemyCount--;

                if (item is Asteroid && !((Asteroid)item).isBullet)
                    asteroidCount--;
                else
                    playerBulletCount--;
            }
        }

        private Boolean onScreen(SpaceItem iCheck)
        {
            Rectangle bounds = camera.screenBounds;
            return (iCheck.centerPosition.X > bounds.Left && iCheck.centerPosition.X < bounds.Right 
                && iCheck.centerPosition.Y > bounds.Top && iCheck.centerPosition.Y < bounds.Bottom);
        }

        private void movePlayer(KeyboardState keystate)
        {
            Vector2 move = Vector2.Zero;
            float rotation = 0.0f;

            if (keystate.IsKeyDown(Keys.Up))
            {
                move.Y--;
            }

            if (keystate.IsKeyDown(Keys.Down))
            {
                move.Y++;
            }

            if (keystate.IsKeyDown(Keys.Right))
            {
                move.X++;
            }

            if (keystate.IsKeyDown(Keys.Left))
            {
                move.X--;
            }

            if (keystate.IsKeyDown(Keys.A))
            {
                rotation--;
            }

            if (keystate.IsKeyDown(Keys.D))
            {
                rotation++;
            }

            //Activate thrusters if player is being moved.
            if (move != Vector2.Zero)
            {
                player.sprite.frameOffsetY = 0;
            }
            else
            {
                player.sprite.frameOffsetY = 64;
            }

            move *= player.accelFactor;
            rotation *= player.rotationFactor;

            player.thrust += move;
            player.rotation += rotation;

        }

        private void giveKillReward(Enemy killed)
        {
            player.gold += killed.gold;
            player.exp += killed.expFactor;
        }

        private void playerAction(GameTime gameTime)
         {
            switch (selectedUpgrade)
            {
                case STOP_ASTEROIDS:
                    if (player.upgrades[STOP_ASTEROIDS].level >= 1)
                        stopAsteroids(gameTime);
                    else
                        notify("You don't have this Upgrade yet!", gameTime);
                    break;
                case ASTEROID_GUN:
                    if (player.upgrades[ASTEROID_GUN].level >= 1)
                        fireAsteroid(gameTime);
                    else
                        notify("You don't have this Upgrade yet!", gameTime);
                    break;
                case EXPLODE_ASTEROIDS:
                    if (player.upgrades[EXPLODE_ASTEROIDS].level >= 1)
                        explodeAsteroids(gameTime);
                    else
                        notify("You don't have this Upgrade yet!", gameTime);
                    break;
                case MAGNET_BEAM:
                    if (player.upgrades[MAGNET_BEAM].level >= 1)
                        fireMagnetBeam(gameTime);
                    else
                        notify("You don't have this Upgrade yet!", gameTime);
                    break;
                default:
                    Console.Error.WriteLine("Invalid weapon selected");
                    return;
            }
        }

        private void stopAsteroids(GameTime gameTime)
        {
            if (gameTime.TotalGameTime - player.upgrades[STOP_ASTEROIDS].lastUsed >= player.upgrades[STOP_ASTEROIDS].cooldown)
            {
                Console.WriteLine("STOP ASTEROID FIRED @ " + gameTime.TotalGameTime);
                player.upgrades[STOP_ASTEROIDS].lastUsed = gameTime.TotalGameTime;
                foreach (SpaceItem item in objectList)
                {
                    if (pow2(item.centerPosition.X - player.centerPosition.X) + pow2(item.centerPosition.Y - player.centerPosition.Y)
                        < pow2(player.upgrades[STOP_ASTEROIDS].level * player.upgrades[STOP_ASTEROIDS].range))
                        item.velocity = Vector2.Zero;
                }
            }
            else
            {
                notify("This Upgrade is on Cooldown", gameTime);
            }
        }

        private void fireAsteroid(GameTime gameTime)
        {
            if (gameTime.TotalGameTime - player.upgrades[ASTEROID_GUN].lastUsed >= player.upgrades[ASTEROID_GUN].cooldown)
            {
                
                player.upgrades[ASTEROID_GUN].lastUsed = gameTime.TotalGameTime;
                Vector2 position = new Vector2((float)(Math.Sin(player.rotation) * (player.radius + asteroidTypes[ASTEROID_BULLET].radius + 2)), 
                    -1 * (float)(Math.Cos(player.rotation) * (player.radius + asteroidTypes[ASTEROID_BULLET].radius + 2)));
                Asteroid bullet = new Asteroid(ASTEROID_BULLET, position + player.centerPosition, position * 3);

                bullet.centerPosition = bullet.position;
                bullet.ratioVelocity = position * 3;

                Console.WriteLine("GUN FIRED @ " + gameTime.TotalGameTime + "ANGLE " + player.rotation);
                objectList.Add(bullet);
                playerBulletCount++;
            }
            else
            {
                notify("This Upgrade is on Cooldown", gameTime);
            }
        }

        private void explodeAsteroids(GameTime gameTime)
        {
            if ((gameTime.TotalGameTime - player.upgrades[EXPLODE_ASTEROIDS].lastUsed >= player.upgrades[EXPLODE_ASTEROIDS].cooldown) && !explOut)
            {
                Console.WriteLine("EXPLODE ASTEROID FIRED @ " + gameTime.TotalGameTime);
                player.upgrades[EXPLODE_ASTEROIDS].lastUsed = gameTime.TotalGameTime;
                player.exploder = true;
            }
            else if (explOut)
            {
                foreach (SpaceItem it in objectList)
                {
                    if (it is Asteroid && it.exploder)
                    {
                        foreach (SpaceItem item in objectList)
                        {
                            if (item is Player)
                                continue;

                            if (pow2(item.centerPosition.X - it.centerPosition.X) + pow2(item.centerPosition.Y - it.centerPosition.Y)
                                < pow2(player.upgrades[EXPLODE_ASTEROIDS].level * player.upgrades[EXPLODE_ASTEROIDS].range))
                            {
                                killList.Add(item);
                                if (item is Enemy)
                                    giveKillReward((Enemy)item);
                            }
                        }
                        killList.Add(it);
                        explOut = false;
                        break;
                    }
                }
            }
            else
            {
                notify("This Upgrade is on Cooldown", gameTime);
            }
        }

        private void fireMagnetBeam(GameTime gameTime)
        {
            if (gameTime.TotalGameTime - player.upgrades[MAGNET_BEAM].lastUsed >= player.upgrades[MAGNET_BEAM].cooldown)
            {
                Console.WriteLine("MAGNET BEAM FIRED @ " + gameTime.TotalGameTime);
                player.upgrades[MAGNET_BEAM].lastUsed = gameTime.TotalGameTime;
                
                int beamLength = player.upgrades[MAGNET_BEAM].range;
                double trueRotation = player.rotation + Math.PI * 3 / 2;

                Vector2 startPos = player.centerPosition + new Vector2((float)Math.Cos(trueRotation) * player.radius, (float)Math.Sin(trueRotation) * player.radius);
                Vector2 endPos = startPos + new Vector2((float)Math.Cos(trueRotation) * beamLength, (float)Math.Sin(trueRotation) * beamLength);

                List<Asteroid> hitList = new List<Asteroid>();
                foreach (SpaceItem item in objectList)
                {
                    if (item is Asteroid && onScreen(item))
                    {
                        double theta1 = Math.Atan2(endPos.Y - startPos.Y, endPos.X - startPos.X);
                        double theta2 = Math.Atan2(item.centerPosition.Y - startPos.Y, item.centerPosition.X - startPos.X);
                        //If the difference between the angles is greater than 90 degrees, the center of this item is not on a line perpendicular to the beam.
                        //This likely means the beam will not intersect the item, but even if it does I'm not interested in the collision.
                        if (Math.Abs(theta2 - theta1) < Math.PI / 2)
                        {
                            double rangeToTarget = Math.Sqrt(pow2(item.centerPosition.Y - startPos.Y) + pow2(item.centerPosition.X - startPos.X));
                            //Check the item's distance from the beam.
                            if (Math.Abs(Math.Sin(Math.Abs(theta2 - theta1))) * rangeToTarget < item.radius)
                            {
                                //Check the item's distance along the beam
                                if (Math.Abs(Math.Cos(Math.Abs(theta2 - theta1))) * rangeToTarget < beamLength)
                                {
                                    hitList.Add((Asteroid)item);
                                    ((Asteroid)item).touched = true;
                                }
                            }
                        }
                    }
                }
                foreach (Asteroid item in hitList)
                {
                    item.isMagnetized = true;
                }
            }
            else
            {
                notify("This Upgrade is on Cooldown", gameTime);
            }
        }
        #endregion

        #region Drawing
        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            // Start drawing
            spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, camera.GetViewMatrix(Vector2.One));
            if (gameStarted)
            {
                spriteBatch.Draw(backgroundColor, camera.screenBounds, Color.White);
                mainBackground.Draw(spriteBatch);

                foreach (SpaceItem item in objectList)
                {
                    item.Draw(spriteBatch);
                }

                foreach (Explosion e in explosionList)
                {
                    if(e != null)
                        e.Draw(spriteBatch);
                }

                foreach (Bullet b in bulletList)
                {
                    b.Draw(spriteBatch);
                }

                //draw magnet beam
                if (gameTime.TotalGameTime - player.upgrades[MAGNET_BEAM].lastUsed < player.upgrades[MAGNET_BEAM].drawTime)
                {
                    Vector2 position = new Vector2((float)(Math.Sin(player.rotation) * (player.radius)),
                        -1 * (float)(Math.Cos(player.rotation) * (player.radius + 2)));
                    spriteBatch.Draw(upgrades[MAGNET_BEAM].texture, player.centerPosition + position, new Rectangle(0, 0, 10, player.upgrades[MAGNET_BEAM].range), Color.White, player.rotation, new Vector2(0, player.upgrades[MAGNET_BEAM].range), 1, new SpriteEffects(), 0);
                   
                }

                drawHUD(gameTime);

                if (gamePaused) //draw the upgrades menu
                {

                    Rectangle view = camera.screenBounds;

                    spriteBatch.Draw(backgroundColor, camera.screenBounds, Color.White);

                    //Draw column headings

                    int x = 0;
                    int y = 0;
                    for (int i = 0; i < 3; i++)
                    {
                        x = view.X + (i * (view.Width / 3)) + 75;
                        y = view.Y + view.Height / 2 - 225;
                        spriteBatch.Draw(genericTexture, new Rectangle(x, y, BUTTON_WIDTH, BUTTON_HEIGHT), Color.Blue);
                        switch (i)
                        {
                            case 0:
                                spriteBatch.DrawString(font, "System", new Vector2(x + 5, y + 5), Color.Black);
                                break;
                            case 1:
                                spriteBatch.DrawString(font, "Upgrades", new Vector2(x + 5, y + 5), Color.Black);
                                break;
                            case 2:
                                spriteBatch.DrawString(font, "Levelups", new Vector2(x + 5, y + 5), Color.Black);
                                break;
                            default:
                                break;
                        }
                    }
                    //Draw all the buttons
                    for (int i = 0; i < NUM_BUTTONS; i++)
                    {
                        drawButton(i);
                    }

                    drawHUD(gameTime);
                    
                }
            }
            else if (gameEnded)
            {
                spriteBatch.Draw(gameOver, camera.screenBounds, Color.White);
            }
            else
            {
                spriteBatch.Draw(titleScreen, camera.screenBounds, Color.White);
            }
            //Stop drawing
            spriteBatch.End();
            //The upgrade can't all be drawn with a single spritebatch.Begin(), so I've moved the code out here to keep it out of the way
            spriteBatch.GraphicsDevice.BlendState = BlendState.AlphaBlend;
            spriteBatch.GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
            drawCooldown(gameTime);
            spriteBatch.GraphicsDevice.BlendState = BlendState.Opaque;
            spriteBatch.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
            base.Draw(gameTime);
        }

        //Draw helpers
        private void drawHUD(GameTime gameTime)
        {
            int X, Y;

            Rectangle view = camera.screenBounds;

            //if (!gamePaused)
            //{
                X = view.Right - BUTTON_WIDTH;
                Y = view.Top;
            //}
            /* No longer used
            else
            {
                X = view.X + (ATTRIB_COL * (view.Width / 3)) + 75;
                Y = view.Y + view.Height / 2 + (-150 + (ATTRIB_BUTTONS) * (BUTTON_HEIGHT + BUTTON_HEIGHT / 2));
            }*/

            spriteBatch.Draw(genericTexture, new Rectangle(X - 5, Y - 5, BUTTON_WIDTH + 10, BUTTON_HEIGHT + 10), Color.Red);

            spriteBatch.Draw(genericTexture, new Rectangle(X, Y, BUTTON_WIDTH, BUTTON_HEIGHT), Color.Black);
            spriteBatch.DrawString(font, "EXP:  " + player.exp + "    Level: " + player.level, new Vector2(X,Y), Color.Purple);
            spriteBatch.DrawString(font, "Gold: " + player.gold + "    Ups: " + playerLevelUps, new Vector2(X, Y + 15), Color.Gold);
            spriteBatch.DrawString(font, "Health: " + player.health + "    " + player.upgrades[selectedUpgrade].shortName, new Vector2(X, Y + 30), Color.Red);

            //Draw the icons. Draw partial cooldown later if need be,since cooldowns need their own begin/end blocks.
            drawIcons(gameTime);

            //Draw current messages.
            int fontHeight = 2 + (int)msgFont.MeasureString("T").Y;//Height of tallest message you could get (and then some).
            X = (int)player.centerPosition.X;
            Y = (int)player.position.Y - fontHeight;
            for (int i = 0; i < notifications.Count; i++)
            {
                String msg = notifications[notifications.Count - 1 - i].message;
                spriteBatch.DrawString(font, msg, new Vector2((int)(X - msgFont.MeasureString(msg).X/2), Y - fontHeight * i), Color.Red);
            }
        }

        public void drawIcons(GameTime gameTime)
        {
            Rectangle view = camera.screenBounds;
            Texture2D icon = null;
            int X;
            int Y;
            for (int i = 0; i < upgradeIcons.Length; i++)
            {
                icon = upgradeIcons[i];
                //Set up locations for icon
                X = view.Left + 5 * (i + 1) + icon.Width * i;
                Y = view.Top + 5;
                //Draw the basic icon
                spriteBatch.Draw(icon, new Rectangle(X, Y, icon.Width, icon.Height), Color.White);
                //Draw red X if you don't have this upgrade
                if (player.upgrades[FIRST_BUYABLE + i].level == 0)
                {
                    spriteBatch.Draw(genericTexture, new Rectangle(X + 32, Y + 32, 64, 5), null, Color.Red, (float)Math.PI / 4.0f, new Vector2(0.5f, 0.5f), new SpriteEffects(), 0);
                    spriteBatch.Draw(genericTexture, new Rectangle(X + 32, Y + 32, 64, 5), null, Color.Red, (float)Math.PI / -4.0f, new Vector2(0.5f, 0.5f), new SpriteEffects(), 0);
                }//Draw greyed out (over the white one) if not selected
                else if (selectedUpgrade != FIRST_BUYABLE + i)
                {
                    spriteBatch.Draw(icon, new Rectangle(X, Y, icon.Width, icon.Height), Color.DarkGray);
                }//Otherwise it's selected and available, so set a variable to draw the cooldown overlay once we get out of this Begin/End block.
                else
                {
                    //This counting stuff is temporary. I just want it to replace the cooldown bar for now.
                    double onCooldown = (gameTime.TotalGameTime - player.upgrades[FIRST_BUYABLE + i].lastUsed).TotalSeconds;
                    if (onCooldown < player.upgrades[FIRST_BUYABLE + i].cooldown.TotalSeconds)
                    {
                        spriteBatch.DrawString(font, String.Format("{0:0}", player.upgrades[FIRST_BUYABLE + i].cooldown.TotalSeconds - onCooldown), new Vector2(X, Y + 64), Color.Red);
                        cooldownToDraw = i;
                    }
                    else
                        cooldownToDraw = -1;
                }
            }
        }

        private void drawCooldown(GameTime gameTime)
        {
            if (cooldownToDraw == -1)
                return;

            //If we really do need to draw a cooldown, then let's do it.
            //Meh, just use a triangle list for now
            int X = 5 * (cooldownToDraw + 1) + 64 * cooldownToDraw;
            int Y = 5;
            double cooldown = player.upgrades[FIRST_BUYABLE + cooldownToDraw].cooldown.TotalMilliseconds;
            //Some junk that visual studio made me put in to make sure my denominator isn't zero
            if (cooldown == 0)
            {
                Console.WriteLine("Mother of god.");
            }
            cooldown = (cooldown == 0) ? 1 : cooldown;
            double pctToDraw = 1.0 - (gameTime.TotalGameTime - player.upgrades[FIRST_BUYABLE + cooldownToDraw].lastUsed).TotalMilliseconds / cooldown;
            if (oldPct < pctToDraw)
                Console.WriteLine("GOT IT! oldPct="+oldPct+" pctToDraw="+pctToDraw);
            oldPct = pctToDraw;
            pctToDraw = (pctToDraw > 1.0f) ? 1.0f : pctToDraw;
            double drawRot = pctToDraw * MathHelper.TwoPi;
            List<VertexPositionColor> trianglePoints = new List<VertexPositionColor>();
            //If top-left section is complete, draw the entire section
            if (drawRot > Math.PI / 4.0)
            {
                trianglePoints.Add(new VertexPositionColor(new Vector3(X, Y, 0), Color.DarkGray));
                trianglePoints.Add(new VertexPositionColor(new Vector3(X + 32, Y, 0), Color.DarkGray));
                trianglePoints.Add(new VertexPositionColor(new Vector3(X + 32, Y + 32, 0), Color.DarkGray));
            }
            else//Otherwise draw a portion of this section and no more
            {
                trianglePoints.Add(new VertexPositionColor(new Vector3(X + 32 - 32 * (float)Math.Tan(drawRot), Y, 0), Color.DarkGray));
                trianglePoints.Add(new VertexPositionColor(new Vector3(X + 32, Y, 0), Color.DarkGray));
                trianglePoints.Add(new VertexPositionColor(new Vector3(X + 32, Y + 32, 0), Color.DarkGray));
            }
            //If left section is complete, draw the entire section
            if (drawRot > 3.0 * Math.PI / 4.0)
            {
                trianglePoints.Add(new VertexPositionColor(new Vector3(X, Y, 0), Color.DarkGray));
                trianglePoints.Add(new VertexPositionColor(new Vector3(X + 32, Y + 32, 0), Color.DarkGray));
                trianglePoints.Add(new VertexPositionColor(new Vector3(X, Y + 64, 0), Color.DarkGray));
            }
            else if (drawRot > Math.PI / 4.0)//Otherwise draw a portion of this section and no more
            {
                trianglePoints.Add(new VertexPositionColor(new Vector3(X, Y, 0), Color.DarkGray));
                trianglePoints.Add(new VertexPositionColor(new Vector3(X + 32, Y + 32, 0), Color.DarkGray));
                trianglePoints.Add(new VertexPositionColor(new Vector3(X, Y + 32 - 32 / (float)Math.Tan(drawRot), 0), Color.DarkGray));
            }
            //If bottom section is complete, draw the entire section
            if (drawRot > 5.0 * Math.PI / 4.0)
            {
                trianglePoints.Add(new VertexPositionColor(new Vector3(X, Y + 64, 0), Color.DarkGray));
                trianglePoints.Add(new VertexPositionColor(new Vector3(X + 32, Y + 32, 0), Color.DarkGray));
                trianglePoints.Add(new VertexPositionColor(new Vector3(X + 64, Y + 64, 0), Color.DarkGray));
            }
            else if (drawRot > 3.0 * Math.PI / 4.0)//Otherwise draw a portion of this section and no more
            {
                trianglePoints.Add(new VertexPositionColor(new Vector3(X, Y + 64, 0), Color.DarkGray));
                trianglePoints.Add(new VertexPositionColor(new Vector3(X + 32, Y + 32, 0), Color.DarkGray));
                trianglePoints.Add(new VertexPositionColor(new Vector3(X + 32 + 32 * (float)Math.Tan(drawRot), Y + 64, 0), Color.DarkGray));
            }
            //If right section is complete, draw the entire section, and any top that must be drawn
            if (drawRot > 7.0 * Math.PI / 4.0)
            {
                //right
                trianglePoints.Add(new VertexPositionColor(new Vector3(X + 64, Y + 64, 0), Color.DarkGray));
                trianglePoints.Add(new VertexPositionColor(new Vector3(X + 32, Y + 32, 0), Color.DarkGray));
                trianglePoints.Add(new VertexPositionColor(new Vector3(X + 64, Y, 0), Color.DarkGray));
                //top
                trianglePoints.Add(new VertexPositionColor(new Vector3(X + 64, Y, 0), Color.DarkGray));
                trianglePoints.Add(new VertexPositionColor(new Vector3(X + 32, Y + 32, 0), Color.DarkGray));
                trianglePoints.Add(new VertexPositionColor(new Vector3(X + 32 - 32 * (float)Math.Tan(drawRot), Y, 0), Color.DarkGray));
            }
            else if (drawRot > 5.0 * Math.PI / 4.0)//Otherwise draw a portion of this section and no more
            {
                trianglePoints.Add(new VertexPositionColor(new Vector3(X + 64, Y + 64, 0), Color.DarkGray));
                trianglePoints.Add(new VertexPositionColor(new Vector3(X + 32, Y + 32, 0), Color.DarkGray));
                trianglePoints.Add(new VertexPositionColor(new Vector3(X + 64, Y + 32 + 32 / (float)Math.Tan(drawRot), 0), Color.DarkGray));
            }
            //First apply the thing
            basicEffect.CurrentTechnique.Passes[0].Apply();
            //Then draw the stuff
            spriteBatch.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, trianglePoints.ToArray(), 0, trianglePoints.Count/3);
        }

        private void drawButton(int buttonID)
        {
            Rectangle view = camera.screenBounds;
            int col = buttonID / 5;
            int row = buttonID % 5;

            //Don't draw if button doesn't exist
            if ((col == SYSTEM_COL && row >= SYSTEM_BUTTONS) || (col == UPGRADE_COL && row >= UPGRADE_BUTTONS) || (col == ATTRIB_COL && row >= ATTRIB_BUTTONS))
                return;

            int X = view.X + (col * (view.Width / 3)) + 75;
            int Y = view.Y + view.Height / 2 + (-150 + row * (BUTTON_HEIGHT + BUTTON_HEIGHT / 2));

            if (selectedButton == buttonID)
            {
                spriteBatch.Draw(genericTexture, new Rectangle(X - 5, Y - 5, BUTTON_WIDTH + 10, BUTTON_HEIGHT + 10), Color.Green);
            }

            //Draw as active or inactive appropriately
            if (buttonsActive[buttonID])
                spriteBatch.Draw(genericTexture, new Rectangle(X, Y, BUTTON_WIDTH, BUTTON_HEIGHT), Color.PowderBlue);
            else
                spriteBatch.Draw(genericTexture, new Rectangle(X, Y, BUTTON_WIDTH, BUTTON_HEIGHT), Color.Gray);

            switch (col)
            {
                case SYSTEM_COL:
                    switch (row)
                    {
                        case FULLSCREEN:
                            spriteBatch.DrawString(font, "Toggle Full Screen", new Vector2(X + 5, Y + 5), Color.Black);
                            break;
                        case SAVE:
                            spriteBatch.DrawString(font, "Save", new Vector2(X + 5, Y + 5), Color.Black);
                            break;
                        case LOAD:
                            spriteBatch.DrawString(font, "Load", new Vector2(X + 5, Y + 5), Color.Black);
                            break;
                        case EXIT:
                            spriteBatch.DrawString(font, "Exit Game", new Vector2(X + 5, Y + 5), Color.Black);
                            break;
                        default:
                            return;
                    }
                    break;
                case UPGRADE_COL:
                    bool maxed = player.upgrades[row + FIRST_BUYABLE].level == player.upgrades[row + FIRST_BUYABLE].maxLevel;
                    spriteBatch.DrawString(font, player.upgrades[row + FIRST_BUYABLE].name, new Vector2(X + 5, Y + 5), Color.Black);
                    spriteBatch.DrawString(font, "Cost: " + (maxed ? 0 : player.upgrades[row + FIRST_BUYABLE].cost) + " Level: " +
                        (maxed ? "Max" : String.Format("{0:0}",player.upgrades[row + FIRST_BUYABLE].level)),
                        new Vector2(X + 5, Y + 25), Color.Black);
                    break;
                case ATTRIB_COL:
                    spriteBatch.DrawString(font, player.upgrades[row].name, new Vector2(X + 5, Y + 5), Color.Black);
                    spriteBatch.DrawString(font, "Cost: 1Up Level: "+player.upgrades[row].level, new Vector2(X + 5, Y + 25), Color.Black);
                    break;
                default:
                    return;
            }

        }
        #endregion

        #region Menu

        protected void buttonAction(int buttonID)
        {
            int col = buttonID / 5;
            int row = buttonID % 5;

            switch(col)
            {
                case SYSTEM_COL:
                    switch(row)
                    {
                        case FULLSCREEN:
                            graphics.ToggleFullScreen();
                            break;
                        case SAVE:
                            Save();
                            break;
                        case LOAD:
                            Load();
                            break;
                        case EXIT:
                            this.Exit();
                            break;
                        default:
                            Console.Error.WriteLine("Invalid button, no action taken");
                            return;
                    }
                    break;
                case UPGRADE_COL:
                    switch(row + FIRST_BUYABLE)
                    {
                        case STOP_ASTEROIDS:
                            getUpgrade(STOP_ASTEROIDS);
                            break;
                        case ASTEROID_GUN:
                            getUpgrade(ASTEROID_GUN);
                            break;
                        case EXPLODE_ASTEROIDS:
                            getUpgrade(EXPLODE_ASTEROIDS);
                            break;
                        case MAGNET_BEAM:
                            getUpgrade(MAGNET_BEAM);
                            break;
                        default:
                            Console.Error.WriteLine("Invalid button, no action taken");
                            return;
                    }
                    break;
                case ATTRIB_COL:
                    switch(row)
                    {
                        case HEALTH:
                            getUpgrade(HEALTH);
                            break;
                        case SPEED:
                            getUpgrade(SPEED);
                            break;
                        case ACCEL:
                            getUpgrade(ACCEL);
                            break;
                        case MASS:
                            getUpgrade(MASS);
                            break;
                        default:
                            Console.Error.WriteLine("Invalid button, no action taken");
                            return;
                    }
                    break;
                default:
                    return;
            }

            player.updateStats();
        }

        protected void getUpgrade(int selectedUpgrade)
        {
            if (selectedUpgrade >= FIRST_BUYABLE)
            {
                if (player.gold >= player.upgrades[selectedUpgrade].cost)
                {
                    player.gold -= player.upgrades[selectedUpgrade].cost;
                    player.upgrades[selectedUpgrade].level++;
                    player.upgrades[selectedUpgrade].cost = player.upgrades[selectedUpgrade].cost * (player.upgrades[selectedUpgrade].level + 1);
                    player.upgrades[selectedUpgrade].cooldown -= player.upgrades[selectedUpgrade].cooldownDec;
                }
            }
            else
            {
                player.upgrades[selectedUpgrade].level++;
                playerLevelUps--;
            }

            updateButtons();
        }

        protected void updateButtons()
        {
            if (playerLevelUps == 0)
            {
                for (int i = 0; i < ATTRIB_BUTTONS; i++)
                {
                    buttonsActive[i + (ATTRIB_COL * 5)] = false;
                }
            }
            else
            {
                for (int i = 0; i < ATTRIB_BUTTONS; i++)
                {
                    buttonsActive[i + (ATTRIB_COL * 5)] = true;
                }
            }

            for (int i = 0; i < UPGRADE_BUTTONS; i++)
            {
                if (player.upgrades[i + FIRST_BUYABLE].cost > player.gold || player.upgrades[i+FIRST_BUYABLE].maxLevel == player.upgrades[i+FIRST_BUYABLE].level)
                {
                    buttonsActive[i + (UPGRADE_COL * 5)] = false;
                }
                else
                {
                    buttonsActive[i + (UPGRADE_COL * 5)] = true;
                }
            }

        }

        public int findNewIndex(int currIndex, int direction)
        {
            if (direction == UP)
            {
                if (currIndex == 0)
                {
                    return currIndex;
                }
                else if (currIndex == 5)
                {
                    return currIndex;
                }
                else if (currIndex == 10)
                {
                    return currIndex;
                }
                else
                {
                    return currIndex - 1;
                }
            }
            else if (direction == DOWN)
            {
                if (currIndex == 3)
                {
                    return currIndex;
                }
                else if (currIndex == 8)
                {
                    return currIndex;
                }
                else if (currIndex == 13)
                {
                    return currIndex;
                }
                else
                {
                    return currIndex + 1;
                }
            }
            else if (direction == LEFT)
            {
                if (currIndex < 5)
                {
                    return currIndex;
                }
                else if (currIndex < 15)
                {
                    return currIndex - 5;
                }
                else
                {
                    return 0;
                }
            }
            else if (direction == RIGHT)
            {
                if (currIndex > 9)
                {
                    return currIndex;
                }
                else if (currIndex > 4)
                {
                    //Return 13 if currentIndex + 5 would put us on an invisible button.
                    return (currIndex + 5 > 13) ? 13 : (currIndex + 5);
                }
                else if (currIndex >= 0)
                {
                    return currIndex + 5;
                }
                else
                {
                    return 0;
                }
            }
            else
            {
                return 0;
            }

        }
        #endregion

        #region Save/Load

        protected void Save()
        {
            TextWriter tw = File.CreateText(saveLoc);

            tw.Write(player.health + " " + player.gold + " " + player.exp + " " + player.level + " ");
            for (int i = 0; i < player.upgrades.Length; i++)
            {
                tw.Write(player.upgrades[i].level + " ");
            }

            tw.Write(nuxMode);
            tw.Close();

            buttonsActive[LOAD] = true;
        }

        protected void Load()
        {
            if (File.Exists(saveLoc))
            {
                StreamReader sr = File.OpenText(saveLoc);
                String[] str = sr.ReadToEnd().Split();

                try
                {
                    player.health = int.Parse(str[0]);
                    player.gold = int.Parse(str[1]);
                    player.exp = int.Parse(str[2]);
                    player.level = int.Parse(str[3]);
                    //Because player upgrades have been changed
                    upgrades.CopyTo(player.upgrades, 0);

                    for (int i = 4; i < str.Length - 2; i++)
                    {
                        player.upgrades[i - 4].level = int.Parse(str[i]);
                    }

                    nuxMode = Boolean.Parse(str[str.Length - 1]);
                    if (nuxMode)
                    {
                        player.health = 9001;
                        player.gold = 9001;
                        shipModels[PLAYERSHIP1].health = 9001;
                    }
                }
                catch(IndexOutOfRangeException)
                {
                    Console.Error.WriteLine("Invalid save file. Possibly from wrong version of the game");
                    buttonsActive[LOAD] = false;
                }

                sr.Close();
            }
        }

        #endregion

        //Helper method to square a number
        public static double pow2(double number)
        {
            return number * number;
        }

        //Helper method to add notifications to list.
        private void notify(String msg, GameTime gameTime)
        {
            notifications.Add(new Notification { message = msg, creationTime = gameTime.TotalGameTime });
        }

    }
}
